/* This file is part of MyRename
 * Copyright (C) 2005-2009 Kai-Chieh Ku (kjackie@gmail.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

// FileListCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "MyRename.h"
#include "MyRenameDlg.h"
#include "others.h"
#include "preferences.h"
#include "language.h"
#include "memdc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define CLR_BLEND(A, B, X) ((A + B * (X-1) + ((X+1)/2)) / X)

#define CLR_RGBBLEND(A, B, X) (                   \
	RGB(CLR_BLEND(GetRValue(A), GetRValue(B), X), \
	CLR_BLEND(GetGValue(A), GetGValue(B), X),     \
	CLR_BLEND(GetBValue(A), GetBValue(B), X))     \
)

// CFileListCtrl

IMPLEMENT_DYNAMIC(CFileListCtrl, CListCtrl)
CFileListCtrl::CFileListCtrl()
	: m_pColOrder(NULL), m_pColWidth(NULL), m_pFriendList(NULL), m_pHeader(NULL)
{
}

CFileListCtrl::~CFileListCtrl()
{
	m_itemImg.Detach(); //do not destroy system image list
}

BEGIN_MESSAGE_MAP(CFileListCtrl, CListCtrl)
	//{{AFX_MSG_MAP(CFileListCtrl)
	ON_WM_DESTROY()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	ON_WM_CONTEXTMENU()
	ON_WM_VSCROLL()
	ON_WM_MOUSEWHEEL()
	ON_WM_ERASEBKGND()
	ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnLvnGetDispInfo)
	ON_NOTIFY_REFLECT(LVN_DELETEITEM, OnLvnDeleteitem)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CFileListCtrl::Init()
{
	m_sCol = 0;
	m_sAsc = true;

	{//load icons --->
		SHFILEINFO shFinfo;

		// Get the system image list using a "path" which is available on all systems.
		VERIFY(m_itemImg.m_hImageList = (HIMAGELIST)SHGetFileInfo(_T("."), 0, &shFinfo, sizeof(SHFILEINFO), SHGFI_SYSICONINDEX | SHGFI_SMALLICON));

		SetImageList(&m_itemImg, LVSIL_SMALL);
	}//load icons <---

	{//load sort arrows --->
		m_pHeader = GetHeaderCtrl();

		if (theApp.m_ullComCtrlVer < MAKEDLLVERULL(6, 0, 0, 0)) { // for older platform
			CBitmap as, ds;
			VERIFY(as.LoadBitmap(IDB_COL_UP));
			VERIFY(ds.LoadBitmap(IDB_COL_DOWN));
			VERIFY(m_colImg.Create(14, 14, ILC_MASK, 0, 2));
			m_colImg.Add(&as, RGB(255, 0, 255));
			m_colImg.Add(&ds, RGB(255, 0, 255));
			// To avoid drawing problems (which occure only with an image list *with* a mask) while
			// resizing list view columns which have the header control bitmap right aligned, set
			// the background color of the image list. (ComCrtl < 6,0,0,0)
			m_colImg.SetBkColor(GetSysColor(COLOR_BTNFACE));
			m_pHeader->SetImageList(&m_colImg);
		}
	}//load sort arrows <---

	{// construct menu --->
		VERIFY(m_context.CreatePopupMenu());
		m_context.AppendMenu(MF_STRING, ID_FILTER_FOLDERS, GetResString(IDS_FOLDERS));
		m_context.AppendMenu(MF_STRING, ID_FILTER_HIDDENFILES, GetResString(IDS_HIDDEN));
		m_context.AppendMenu(MF_STRING, ID_FILTER_READONLYFILES, GetResString(IDS_READONLY));
		m_context.AppendMenu(MF_STRING, ID_FILTER_SYSTEMFILES, GetResString(IDS_SYSTEM));
		m_context.AppendMenu(MF_SEPARATOR);
		m_context.AppendMenu(MF_STRING, ID_EDIT_COPY, GetResString(IDS_COPY));
		m_context.AppendMenu(MF_SEPARATOR);
		m_context.AppendMenu(MF_STRING, ID_EDIT_SELECT_ALL, GetResString(IDS_SELECT_ALL));
		m_context.AppendMenu(MF_SEPARATOR);
		m_context.AppendMenu(MF_STRING, IDR_AUTO_SCROLL, GetResString(IDR_AUTO_SCROLL));
	}// construct menu <---

	SetExtendedStyle(GetExtendedStyle() | LVS_EX_BORDERSELECT | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP);

	if (GetExtendedStyle() & LVS_EX_BORDERSELECT)
		ModifyStyle(0, LVS_OWNERDRAWFIXED);
	else
		ModifyStyle(LVS_OWNERDRAWFIXED, 0);

	SetOutlineColor(GetSysColor(COLOR_HIGHLIGHT));
}

void CFileListCtrl::Localize()
{
	m_context.ModifyMenu(ID_FILTER_FOLDERS, MF_BYCOMMAND, ID_FILTER_FOLDERS, GetResString(IDS_FOLDERS));
	m_context.ModifyMenu(ID_FILTER_HIDDENFILES, MF_BYCOMMAND, ID_FILTER_HIDDENFILES, GetResString(IDS_HIDDEN));
	m_context.ModifyMenu(ID_FILTER_READONLYFILES, MF_BYCOMMAND, ID_FILTER_READONLYFILES, GetResString(IDS_READONLY));
	m_context.ModifyMenu(ID_FILTER_SYSTEMFILES, MF_BYCOMMAND, ID_FILTER_SYSTEMFILES, GetResString(IDS_SYSTEM));
	m_context.ModifyMenu(ID_EDIT_COPY, MF_BYCOMMAND, ID_EDIT_COPY, GetResString(IDS_COPY));
	m_context.ModifyMenu(ID_EDIT_SELECT_ALL, MF_BYCOMMAND, ID_EDIT_SELECT_ALL, GetResString(IDS_SELECT_ALL));
	m_context.ModifyMenu(IDR_AUTO_SCROLL, MF_BYCOMMAND, IDR_AUTO_SCROLL, GetResString(IDR_AUTO_SCROLL));

	HDITEM hdi;
	hdi.mask = HDI_TEXT;

#define SETCOL(n, id) {strRes = GetResString(id);\
		hdi.pszText = strRes.GetBuffer();\
		m_pHeader->SetItem(n, &hdi);\
		strRes.ReleaseBuffer();}

	if (m_pHeader->GetItemCount() > 0) {
		CString strRes;

		SETCOL(0, IDS_COL_NAME)
		SETCOL(1, IDS_COL_TYPE)
	}

#undef SETCOL

}

void CFileListCtrl::SetColumns(CUIntArray *order, CUIntArray *width)
{
	if (order->GetCount() < MAXCOL || width->GetCount() < MAXCOL) { // use default value in width and order
		order->SetSize(MAXCOL);
		width->SetSize(MAXCOL);
		order->SetAt(0, 0);
		order->SetAt(1, 1);
		width->SetAt(0, 200);
		width->SetAt(1, 70);
	}

	int i = 0;

	InsertColumn(i, GetResString(IDS_COL_NAME), LVCFMT_LEFT, width->GetAt(i)); i++;
	InsertColumn(i, GetResString(IDS_COL_TYPE), LVCFMT_LEFT, width->GetAt(i)); i++;

	const int count = m_pHeader->GetItemCount();

	LPINT porder = new int [count];
	
	for (i = 0;i < count;i++)
		porder[i] = order->GetAt(i);
	m_pHeader->SetOrderArray(count, porder);

	delete [] porder;

	m_pColOrder = order;
	m_pColWidth = width;
}

void CFileListCtrl::CreateList(LPCTSTR path, const CFileName *mask /* = NULL */)
{
	CString _path(path);
	CFileFind finder;
	if (mask == NULL || mask->GetName() == NULL) _path += _T("*.*");
	else _path += mask->GetName();

	DeleteAllItems();

	if (!PathFileExists(path))
		return;

	BOOL bFound = finder.FindFile(_path);

	while (bFound) {
		bFound = finder.FindNextFile();
		if (!finder.IsDots() && (mask == NULL ||
			!(
				mask->isFolder() && finder.IsDirectory() ||
				mask->isHidden() && finder.IsHidden() ||
				mask->isReadOnly() && finder.IsReadOnly() ||
				mask->isSystem() && finder.IsSystem()
			)
			))

			InsertItem(finder.GetFileName(), FHRS(finder.IsDirectory(), finder.IsHidden(), finder.IsReadOnly(), finder.IsSystem()), GetFileIcon(finder.GetFilePath()));
	}

	SortList();
}

void CFileListCtrl::CreateList(const CFileListCtrl &src, transfunc trfun /* = NULL */)
{
	int count = src.GetItemCount(), i;
	CFileName *data;
	CString newName;
	bool sel = false, *selectlist = NULL;
	POSITION selpos = src.GetFirstSelectedItemPosition();

	DeleteAllItems();

	if (selpos != NULL) {
		sel = true;
		selectlist = new bool [count];
		for (i = 0;i < count;i++) {
			selectlist[i] = false;
		}
		while (selpos != NULL) {
			selectlist[src.GetNextSelectedItem(selpos)] = true;
		}
	}

	for (i = 0;i < count;i++) {
		data = (CFileName *) src.GetItemData(i);
		if (trfun != NULL && (!sel || selectlist[i])) {
			trfun(newName, data->GetName(), data->isFolder());
			InsertItem(newName, data->m_attribute, data->m_iIcon);
		}
		else
			InsertItem(data->GetName(), data->m_attribute, data->m_iIcon);
	}

	if (selectlist)
		delete [] selectlist;
}

void CFileListCtrl::InsertItem(LPCTSTR name, uint16 attr, int iIcon, int nItem /* = -1 */)
{
	CFileName *theItem = new CFileName(name, attr);
	theItem->m_iIcon = iIcon;
	if (nItem == -1) nItem = GetItemCount();
	CListCtrl::InsertItem(LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE, nItem, LPSTR_TEXTCALLBACK, 0, 0, I_IMAGECALLBACK, (LPARAM) theItem);
}

bool CFileListCtrl::SortList(int col /* = -1 */)
{
	if (col < 0)
		SortList(m_sCol, m_sAsc);
	else if (col == m_sCol) {
		// no need to sort again; instead, reverse the list. (inverse case is the worst case in some sort algorithm)
		ReverseList();
		SetSortArrow(col, !m_sAsc);
		m_sAsc = !m_sAsc;
		return true;
	}
	else
		SortList(col, true);

	return false;
}

void CFileListCtrl::SortList(int col, bool bAsc)
{
	uint32 sortSet;

	sortSet = (col << 1) | (bAsc ? 0 : 1);
	SortItems(ColCmp, sortSet);
	SetSortArrow(col, bAsc);
	m_sCol = col;
	m_sAsc = bAsc;
}

void CFileListCtrl::ReverseList()
{
	int count = GetItemCount(), i, state1, state2;
	CFileName *pData1, *pData2;
	for (i = 0;i < (count >> 1);i++) {
		pData1 = (CFileName *) GetItemData(i);
		pData2 = (CFileName *) GetItemData(count - i - 1);
		state1 = GetItemState(i, UINT_MAX);
		state2 = GetItemState(count - i - 1, UINT_MAX);
		SetItem(i, 0, LVIF_PARAM | LVIF_STATE, NULL, 0, state2, UINT_MAX, (LPARAM) pData2);
		SetItem(count - i - 1, 0, LVIF_PARAM | LVIF_STATE, NULL, 0, state1, UINT_MAX, (LPARAM) pData1);
	}
}

void CFileListCtrl::SetSortArrow(int nCol, bool bAsc)
{
	HDITEM col;

	col.mask = HDI_FORMAT;
	col.fmt = HDF_STRING;
	m_pHeader->SetItem(m_sCol, &col);

	if (theApp.m_ullComCtrlVer < MAKEDLLVERULL(6, 0, 0, 0)) { // for older platform
		col.mask |= HDI_IMAGE;
		col.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT;
		col.iImage = bAsc ? 0 : 1;
	}
	else
		col.fmt |= bAsc ? HDF_SORTUP : HDF_SORTDOWN;

	m_pHeader->SetItem(nCol, &col);

	m_sCol = nCol;
}

#define FCMP(a, b) ((a->isFolder() == b->isFolder()) ? 0 : (a->isFolder() && !b->isFolder()) ? -1 : 1)

int CFileListCtrl::ColCmp(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CFileName *strItem1 = (CFileName *) lParam1;
	CFileName *strItem2 = (CFileName *) lParam2;
	strcmpfunc cmpstrfunc = thePrefs.bSmartSort ? smartstricmp : sstricmp;
	int cmp;

	switch ((uint32) lParamSort) {
		case 0:
			if (cmp = FCMP(strItem1, strItem2))
				return cmp;
			return cmpstrfunc(*strItem1, *strItem2);
		case 1:
			if (cmp = FCMP(strItem2, strItem1))
				return cmp;
			return cmpstrfunc(*strItem2, *strItem1);
		case 2:
			if (cmp = FCMP(strItem1, strItem2))
				return cmp;
			return cmpstrfunc(strItem1->GetType(), strItem2->GetType());
		case 3:
			if (cmp = FCMP(strItem2, strItem1))
				return cmp;
			return cmpstrfunc(strItem2->GetType(), strItem1->GetType());
	}

	return 0;
}

#undef FCMP

void CFileListCtrl::UpdateMenu()
{
	m_context.CheckMenuItem(ID_FILTER_FOLDERS, MF_BYCOMMAND | (thePrefs.fnMask.isFolder() ? MF_UNCHECKED : MF_CHECKED));
	m_context.CheckMenuItem(ID_FILTER_HIDDENFILES, MF_BYCOMMAND | (thePrefs.fnMask.isHidden() ? MF_UNCHECKED : MF_CHECKED));
	m_context.CheckMenuItem(ID_FILTER_READONLYFILES, MF_BYCOMMAND | (thePrefs.fnMask.isReadOnly() ? MF_UNCHECKED : MF_CHECKED));
	m_context.CheckMenuItem(ID_FILTER_SYSTEMFILES, MF_BYCOMMAND | (thePrefs.fnMask.isSystem() ? MF_UNCHECKED : MF_CHECKED));
	m_context.EnableMenuItem(ID_EDIT_COPY, MF_BYCOMMAND | (GetFirstSelectedItemPosition() == NULL ? MF_GRAYED : MF_ENABLED));
	m_context.CheckMenuItem(IDR_AUTO_SCROLL, MF_BYCOMMAND | (thePrefs.bExtAutoScroll ? MF_CHECKED : MF_UNCHECKED));
}

void CFileListCtrl::Synchronize()
{
	if (!thePrefs.bExtAutoScroll || m_pFriendList == NULL)
		return;
	int thisTop, thatTop;

	CRect ItemRc;
	thisTop = GetTopIndex();
	thatTop = m_pFriendList->GetTopIndex();
	m_pFriendList->GetItemRect(thatTop, ItemRc, LVIR_BOUNDS);
	m_pFriendList->Scroll(CSize(0, ItemRc.Height() * (thisTop - thatTop)));
}

void CFileListCtrl::RedrawVisible()
{
	int top = GetTopIndex();
	RedrawItems(top, min(GetItemCount() - 1, top + GetCountPerPage() + 1));
	UpdateWindow();
}

// CFileListCtrl message handlers


void CFileListCtrl::OnDestroy()
{
	m_colImg.DeleteImageList();
	m_context.DestroyMenu();

	if (m_pColOrder != NULL && m_pColWidth != NULL) {
		CRect theItemRc;
		int count = m_pHeader->GetItemCount(), i;
		LPINT porder = new int [count];
		m_pHeader->GetOrderArray(porder, count);
		m_pColOrder->SetSize(count);
		m_pColWidth->SetSize(count);
		for (i = 0;i < count;i++) {
			m_pHeader->GetItemRect(i, theItemRc);
			m_pColOrder->SetAt(i, porder[i]);
			m_pColWidth->SetAt(i, theItemRc.Width());
		}
		delete [] porder;
	}

	CListCtrl::OnDestroy();
}

void CFileListCtrl::OnSetFocus(CWnd* pOldWnd)
{
	CListCtrl::OnSetFocus(pOldWnd);
}

void CFileListCtrl::OnKillFocus(CWnd* pNewWnd)
{
	CListCtrl::OnKillFocus(pNewWnd);
}

void CFileListCtrl::OnContextMenu(CWnd* pWnd, CPoint point)
{
	if (point.x == -1 || point.y == -1) { //the context menu notify is from keyboard
		POSITION pos = GetFirstSelectedItemPosition();
		int selItem;
		CRect rc, rcWin;

		GetClientRect(rcWin);
		try {
			if (pos) {
				selItem = GetNextSelectedItem(pos);
				if (GetItemRect(selItem, rc, LVIR_BOUNDS) && (rcWin.PtInRect(rc.TopLeft()) 
					|| rcWin.PtInRect(rc.BottomRight()))) //the item is visible
				{
					point = rc.CenterPoint();
					ClientToScreen(&point);
				}
				else
					throw 1;
			}
			else
				throw 1;
		} catch (const int ex) {
			if (ex == 1) {
				GetWindowRect(rc);
				point = rc.TopLeft();
			}
			else
				throw ex;
		}
	}

	UpdateMenu();

	m_context.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
}

void CFileListCtrl::OnEditCopy()
{
	POSITION pos = GetFirstSelectedItemPosition();
	if (pos == NULL)
		return;
	if (!OpenClipboard()) {
		MessageBox(_T("Can not open clip board!!"));
		return;
	}

	CString copydata;
	int i;
	while (pos) {
		i = GetNextSelectedItem(pos);
		if (!copydata.IsEmpty()) copydata += _T("\r\n");
		copydata += ((CFileName *) GetItemData(i))->GetName();
	}

	HGLOBAL data;
	LPTSTR ptr;
	size_t len = copydata.GetLength();

	data = GlobalAlloc(GMEM_MOVEABLE, sizeof(TCHAR) * (len + 1));
	ptr = (LPTSTR) GlobalLock(data);
	memcpy(ptr, copydata.GetBuffer(), sizeof(TCHAR) * (len + 1));
	copydata.ReleaseBuffer();
	ptr[len] = _T('\0');
	GlobalUnlock(data);

	if (SetClipboardData(CF_UNICODETEXT, data) == NULL)
		MessageBox(_T("Unable to set Clipboard data!!"));

	CloseClipboard();
}

void CFileListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
	Synchronize();
}

BOOL CFileListCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	BOOL bResult = CListCtrl::OnMouseWheel(nFlags, zDelta, pt);
	Synchronize();
	return bResult;
}

BOOL CFileListCtrl::OnCommand(WPARAM wParam, LPARAM lParam)
{
	switch (HIWORD(wParam)) {
		case 0: //from menu items
		case 1: //from accelerators
			switch (LOWORD(wParam)) {
				case ID_FILTER_FOLDERS:
				case ID_FILTER_HIDDENFILES:
				case ID_FILTER_READONLYFILES:
				case ID_FILTER_SYSTEMFILES:
					MAINWND->SendMessage(WM_COMMAND, wParam);
					return TRUE;
				case IDR_AUTO_SCROLL:
					thePrefs.bExtAutoScroll = !thePrefs.bExtAutoScroll;
					return TRUE;
				case ID_EDIT_UNDO:
					MAINWND->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
					return TRUE;
				case ID_EDIT_SELECT_ALL:
					{
						int count = GetItemCount(), i;
						for (i = 0;i < count;i++)
							SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
					}
					return TRUE;
				case ID_EDIT_COPY:
					OnEditCopy();
					return TRUE;
			}
			break;
	}

	return CListCtrl::OnCommand(wParam, lParam);
}

void CFileListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	CRect rcIcon, rcLabel, rcBounds, rcItem = lpDrawItemStruct->rcItem;
	CImageList *pImgList = GetImageList(LVSIL_SMALL);
	bool bSel = (lpDrawItemStruct->itemState & ODS_SELECTED) != 0;
	bool bFocus = (lpDrawItemStruct->itemState & ODS_FOCUS) != 0;
	int iItem = lpDrawItemStruct->itemID, count = GetItemCount(), oldBkMode, i;
	CFont *pOldFont;

	CMemDC dc(CDC::FromHandle(lpDrawItemStruct->hDC), rcItem);

	GetItemRect(iItem, rcBounds, LVIR_BOUNDS);

	if (bSel/* || bFocus*/) {//remove this comment to outline the focus item
		COLORREF crOL = GetOutlineColor(), crHL = CLR_RGBBLEND(crOL, GetBkColor(), 4);
		CRect rcOutline = rcBounds;
		CBrush brOL(crOL), brHL(crHL);

		dc.FrameRect(rcOutline, &brOL); //draw the border

		if (bSel) {//&& !bFocus
			rcOutline.left++;
			rcOutline.right--;
			rcOutline.top++;
			rcOutline.bottom--;

			//the previous item is selected (erase the top border)
			if (iItem > 0 && GetItemState(iItem - 1, LVIS_SELECTED))
				rcOutline.top--;
			//the next item is selected (erase the bottom border)
			if (iItem < count - 1 && GetItemState(iItem + 1, LVIS_SELECTED))
				rcOutline.bottom++;

			dc.FillRect(rcOutline, &brHL); //draw the highlight background
		}
	}

	GetItemRect(iItem, rcIcon, LVIR_ICON);

	{
		LVITEM lvItem = {0};
		lvItem.iItem = iItem;
		lvItem.mask = LVIF_IMAGE;
		GetItem(&lvItem);
		pImgList->Draw(&dc, lvItem.iImage, rcIcon.TopLeft(), ILD_TRANSPARENT | (bSel ? ILD_SELECTED : (/*bFocus ? ILD_FOCUS : */0))); //remove this comment to draw blend image on focus item
	}

	pOldFont = dc.SelectObject(GetFont());
	oldBkMode = dc.SetBkMode(TRANSPARENT);

	for (i = 0;i < MAXCOL;i++) {
		GetSubItemRect(iItem, i, LVIR_LABEL, rcLabel);
		rcLabel.left += 4;
		dc.DrawText(GetItemText(iItem, i), rcLabel, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_NOCLIP | DT_NOPREFIX);
	}

	dc.SelectObject(pOldFont);
	dc.SetBkMode(oldBkMode);
}

//flicker free
BOOL CFileListCtrl::OnEraseBkgnd(CDC* pDC)
{
	int itemCount = GetItemCount();
	if (!itemCount)
		return CListCtrl::OnEraseBkgnd(pDC);

	RECT clientRect;
	RECT itemRect;
	int topIndex = GetTopIndex();
	int maxItems = GetCountPerPage();
	int drawnItems = itemCount < maxItems ? itemCount : maxItems;
	CRect rcClip;

	//draw top portion
	GetClientRect(&clientRect);
	rcClip = clientRect;
	GetItemRect(topIndex, &itemRect, LVIR_BOUNDS);
	clientRect.bottom = itemRect.top;
	if (GetBkColor() != CLR_NONE)
		pDC->FillSolidRect(&clientRect, GetBkColor());
	else
		rcClip.top = itemRect.top;

	//draw bottom portion if we have to
	if(topIndex + maxItems >= itemCount) {
		GetClientRect(&clientRect);
		GetItemRect(topIndex + drawnItems - 1, &itemRect, LVIR_BOUNDS);
		clientRect.top = itemRect.bottom;
		rcClip.bottom = itemRect.bottom;
		if (GetBkColor() != CLR_NONE)
			pDC->FillSolidRect(&clientRect, GetBkColor());
	}

	//draw right half if we need to
	if (itemRect.right < clientRect.right) {
		GetClientRect(&clientRect);
		clientRect.left = itemRect.right;
		rcClip.right = itemRect.right;
		if (GetBkColor() != CLR_NONE)
			pDC->FillSolidRect(&clientRect, GetBkColor());
	}

	if (GetBkColor() == CLR_NONE){
		CRect rcClipBox;
		pDC->GetClipBox(&rcClipBox);
		rcClipBox.SubtractRect(&rcClipBox, &rcClip);
		if (!rcClipBox.IsRectEmpty()){
			pDC->ExcludeClipRect(&rcClip);
			CListCtrl::OnEraseBkgnd(pDC);
			InvalidateRect(&rcClip, FALSE);
		}
	}
	return TRUE;
}

void CFileListCtrl::OnLvnGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
{
	NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);

	CFileName *data = (CFileName *) pDispInfo->item.lParam;
	if (pDispInfo->item.mask & LVIF_TEXT) {
		switch (pDispInfo->item.iSubItem) {
			case 0:
				pDispInfo->item.pszText = (LPTSTR) data->GetName();
				break;
			case 1:
				pDispInfo->item.pszText = data->GetType() ? (LPTSTR) data->GetType(-1) : _T("");
				break;
		}
	}
	if (pDispInfo->item.mask & LVIF_IMAGE)
		pDispInfo->item.iImage = data->m_iIcon;

	*pResult = 0;
}


void CFileListCtrl::OnLvnDeleteitem(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);

	CFileName *theItem = (CFileName *) GetItemData(pNMLV->iItem);
	delete theItem;

	*pResult = 0;
}


LRESULT CFileListCtrl::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {
		case WM_KEYDOWN:
		case WM_SYSKEYDOWN:
			{
				if (IsCtrlPressed())
					switch (wParam) {
						case 'A':
							SendMessage(WM_COMMAND, 0x00010000 | ID_EDIT_SELECT_ALL);
							return TRUE;
						case 'C':
							SendMessage(WM_COMMAND, 0x00010000 | ID_EDIT_COPY);
							return TRUE;
						case 'Z':
							SendMessage(WM_COMMAND, 0x00010000 | ID_EDIT_UNDO);
							return TRUE;
					}
			}
			break;
	}

	return CListCtrl::DefWindowProc(message, wParam, lParam);
}
